-- Flow Control --

forall .a .b :- not (.a, not .b) #

whatever .q :- .q ! #
whatever . #

finally .a .b :- .a; .b, fail ! .b #

io.order .i:.o .cls :- bound .i ! .cls #
io.order .i:.o (.cl1, .cl2) :- .cl2, .cl1 #


-- Utilities --

free .v :- not (bound .v) #


-- Counting & Temporaries --

sum .a .b .s :- free .s, int .a, int .b, let .s (.a + .b) #
sum .a .b .s :- free .a, int .b, int .s, let .a (.s - .b) #
sum .a .b .s :- free .b, int .a, int .s, let .b (.s - .a) #

getOrDefault .k .v . :- get .k .v ! #
getOrDefault . .v .def :- .v = .def #

count .name .n :-
	synchronized .name (
		getOrDefault .name .n 0,
		let .n1 (.n + 1),
		set .name .n1
	) #

temporary .n :- count TEMP .n #
temporary .name .t :- temporary .n, concat .name .n .t #

any .v .pred :-
	temporary .v,
	finally .pred (unset .v) #

globals.dump :-
	forall (get.all .name .value) (
		write .name "=" .value, nl
	) #


-- Find Alls --

last (.last, $) $ .last #
last (.head, .tail) (.head, .tail0) .last :-
	last .tail .tail0 .last #

	-- Faster, but not in order (actually reverse)
findall.unordered .var .cb .list :-
	any .name (
		set .name $,
		forall (.cb, get .name .l) (set .name (.var, .l)),
		get .name .list
	) #

findall .var .cb .list :-
	any .name (
		set .name $,
		forall (
			.cb,
			get .name .l,
			last .l1 .l .var
		) (set .name .l1),
		get .name .list
	) #


-- Managing Rules --

rule.callSign .rule .type :-
	rule.headTail .rule .head .,
	head.callSign .head .type #

rule.headTail .fact .fact $ :-
	not (bound .fact, .fact = (. :- .)) ! #
rule.headTail (.q :- .c) .q .c #

head.callSign [] ($ / 0) #
head.callSign .tuple (.name / .len) :-
	length .tuple .len,
	is.tuple .tuple,
	nth .tuple 0 .first,
	head.callSign .first (.name / .) #
head.callSign .rule (.rule / 1) :-
	not (is.tuple .rule) #

member.reverse (., .tail) .element :-
	member.reverse .tail .element #
member.reverse (.element, .) .element #

	-- This only works for first-order clauses
asserta .new :-
	rule.callSign .new .sign,
	findall.unordered .rule (
		rule.callSign .rule .sign,
		rule .rule,
		once (retract .rule)
	) .rules,
	assert .new,
	forall (member.reverse .rules .rule) (assert .rule) ! #

export .pattern .filename :-
	stream.writeFile .stream .filename,
	forall (rule .pattern) (
		stream.dump .stream .pattern,
		stream.put .stream " #",
		stream.nl .stream
	),
	stream.close .stream #


-- Debugging --

ls :- exec "ls" #
ls .file :- exec ("ls" .file) #

vi .file :- exec ("vi" .file) #

timing :- exec "date +%%H:%%M:%%S.%%N" #

timing .text :-
	stream.stdout OUT,
	stream.write OUT .text, stream.flush OUT,
	stream.close OUT,
	timing #

	-- Invocation trace; call these before anything imported
trace.calls :- assertTrace "TRACE:" . #

trace.predicate .call :- assertTrace "TRACE:" .call #

	-- Fail trace; call these after everything imported
trace.fails :- assertTrace "FAILED:" . #

assertTrace .tag .call :-
	assert (.call :- write .tag, dump .call, nl, fail) #


-- Rules Importer --

IMPORTED "%%/auto.sl" # -- This can suppress 'undefined clause' warning

import .f :- -- Simpler version, used before adapting the meta (complicated) one
	if (not IMPORTED .f) then (
		assert (IMPORTED .f),
		load .f
	) ! #

:-	home.dir .home,
	concat .home "/meta/import.sl" .file,
	load .file #

:- import "%%/meta/bytecode.sl" #
